/**
 * 
 */
package com.java8.lambda;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.DoubleSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.jupiter.api.Test;

/**
 * Stream流操作-java8新特性
 * @author zhangyang
 *
 */
public class StreamTest {

	//@Test
	public void test1() {
		//创建stream
		//1.通过Collection提供的stream()方法获取流
		List<String> list=new ArrayList<>();
		Stream<String> stream2 = list.stream();
		//2.通过Arrays的Stream方法获取得到流
		Stream<Employee> stream = Arrays.stream(new Employee[10]);
		//3.通过Stream类的静态方法of也能得到流
		Stream<String> of = Stream.of("11","12");
		//4.通过Stream的静态方法iterate或者generate创建无限流
		/**
		 * 第一种方法
		 * 迭代-Stream的迭代方法iterate能无限迭代对象产生流
		 * 需要的参数-种子,UnaryOperator (函数式接口Function)
		 * limit方法 最大个数
		 */
		Stream.iterate(0, x->x+2).limit(10).forEach(System.out::println);
		/**
		 * 第二种方法
		 * 生成-Stream的generate方法能创建无限流
		 */
		Stream.generate(()->Math.random()).limit(10).forEach(System.out::println);
	}
	//@Test
	public void test2() {
		/**
		 * Stream流操作 api
		 * 基础方法 forEach(循环)
		 * Stream中间操作会惰性求值，延迟加载。多个中间操作会连接起来形成一个流水线
		 * 除非流水线上触发终止操作，负责中间操作不会做任何处理。而是在终止操作时一次性处理
		 * Stream提供api有内部迭代动作，不需要写迭代操作，Stream会自动帮我们循环
		 */
		/**
		 * 1.筛选与切片-
		 * filter-筛选Lambda,从中筛选出元素
		 * limit-截断流使其不操作某个数量
		 * skip-返回一个扔掉指定元素个数的流，如果不超过这个元素院会一个空流-和Limit互补
		 * distinct-元素去重 但是必须重写hashCode和equals才能去重
		 */
		List<Employee> list = Arrays.asList(new Employee("张三", 12),
				new Employee("李四", 15),
				new Employee("王五", 12),
				new Employee("赵六", 33),
				new Employee("赵六", 33),
				new Employee("张琪", 33),
				new Employee("李白", 25)
				);
		Stream<Employee> st = list.stream().filter(x->{
			System.out.println("流操作执行");
			return x.getAge()>20;
		}).limit(2);
		System.out.println("======filter和limit=======");
		st.forEach(System.out::println);
		Stream<Employee> st1 = list.stream().filter(x->{
			System.out.println("流操作执行");
			return x.getAge()>20;
		}).skip(2);
		System.out.println("======skip=======");
		st1.forEach(System.out::println);
		Stream<Employee> st2 = list.stream().filter(x->{
			return x.getAge()>20;
		}).distinct();
		System.out.println("======去重=======");
		st2.forEach(System.out::println);
	}
	//@Test
	public void test3() {
		/**
		 * 映射方法 将流里面的一个元素变成另一个元素
		 */
		/**
		 * map -传入一个lambda，将元素转换成其他的形式或者提取信息。
		 * 该函数会应用到所有元素上将每一个元素映射成为新的元素
		 */
		List<Employee> list = Arrays.asList(new Employee("张三", 12),
				new Employee("李四", 15),
				new Employee("王五", 12),
				new Employee("赵六", 33),
				new Employee("赵六", 33),
				new Employee("张琪", 33),
				new Employee("李白", 25)
				);
		//集合的某个元素提取出来形成新的集合
		List<String> list2 = list.stream().map(Employee::getName).collect(Collectors.toList());
		list2.forEach(System.out::println);
		//集合转map-使用Collectors的toMap方法转换
		Map<String, Employee> collect = list.stream()
				.collect(Collectors.toMap(Employee::getName, Function.identity(),(k1,k2)->k2));
		collect.values().forEach(System.out::println);
		/**
		 * flatmap-扁平化映射，
		 * 将流中的元素转换成另一个元素,然后将另一个元素形成的流合并成为一个新流
		 * 只要使用场景就是在于集合嵌套时，按照条件最后取消原本的集合嵌套得到一个新的集合
		 * 这里类似于集合的add和addAll方法区别
		 */
		List<String> asList = Arrays.asList("aaa","bbb","ccc","ddd");
		//将集合的每一个元素拆分按照字母拆分新的集合
		Stream<Stream<Character>> map = asList.stream().map(StreamTest::filterCharacter);
		//将这些集合元素分别显示
		map.forEach(item->{
			item.forEach(System.out::println);
		});
		System.out.println("==========flatMap===========");
		asList.stream().flatMap(StreamTest::filterCharacter).forEach(System.out::println);
	}
	
	/**
	 * 字符串转成流
	 * @param str
	 * @return
	 */
	public static Stream<Character> filterCharacter(String str){
		List<Character> list=new ArrayList<>();
		char[] charArray = str.toCharArray();
		for(char chr:charArray) {
			list.add(chr);
		}
		return list.stream();
	}
	//@Test
	public void test4() {
		/**
		 * 排序-自然排序 comparable和定制排序Comparator
		 * sorted方法 无参数和排序参数
		 */
		List<Employee> list = Arrays.asList(new Employee("张三", 12),
				new Employee("李四", 15),
				new Employee("王五", 12),
				new Employee("赵六", 33),
				new Employee("赵六", 33),
				new Employee("张琪", 33),
				new Employee("李白", 25)
				);
		//自然排序
		list.stream().map(Employee::getAge).sorted().forEach(System.out::println);
		//定制排序
		list.stream().sorted((x,y)->{
			if(x.getAge()==y.getAge()) {
				return x.getName().compareTo(y.getName());
			}else {
				return Integer.compare(x.getAge(), y.getAge());
			}
		}).forEach(System.out::println);
	}
	
	//@Test
	public void test5() {
		/**
		 * 查找与匹配
		 * allMatch 检查是否匹配所有元素
		 * anyMatch 是否匹配一个元素
		 * noneMatch-检查是否没有匹配所有元素
		 * findFirst 按照条件查找第一个元素 配合排序使用
		 * 	Optional 可能为空的判断
		 * findAny 查找流中任意一个元素
		 *  Optional 可能为空的判断
		 * count 获取流中元素的总数
		 * min 获取流中元素的最小值-配合比较使用
		 * max获取流中元素的最大值-配合比较使用
		 */
		
		List<Employee> list = Arrays.asList(new Employee("张三", 12),
				new Employee("李四", 15),
				new Employee("王五", 12),
				new Employee("赵六", 33),
				new Employee("赵六", 33),
				new Employee("张琪", 33),
				new Employee("李白", 25)
				);
		boolean match = list.stream().allMatch(item->item.getName().equals("张三"));
		match=list.stream().allMatch(item->item.getAge()>0 && item.getAge()<100);
		System.out.println(match);
		match=list.stream().allMatch(item->item.getAge()>0 && item.getAge()<100);
		System.out.println("====年龄匹配allMatch==="+match);
		match=list.stream().anyMatch(item->item.getName().equals("张三"));
		System.out.println("=====anyMatch==="+match);
		match=list.stream().noneMatch(item->item.getName().equals("王二麻子"));
		System.out.println("====noneMatch全部匹配不上"+match);
		Optional<Employee> findFirst = list.stream().sorted((x,y)->Integer.compare(x.getAge(), y.getAge())).findFirst();
		System.out.println("=========findFirst="+findFirst.get());
		Optional<Employee> findAny = list.stream().filter(item->item.getName().equals("赵六")).findAny();
		System.out.println("=========findAny="+findAny.get());
		long count = list.stream().filter(item->item.getName().equals("赵六")).count();
		Optional<Employee> min = list.stream().filter(item->item.getName().equals("赵六")).min((x,y)->Integer.compare(x.getAge(), y.getAge()));
		System.out.println("=========min="+min.get());
	}
	@Test
	public void test6() {
		List<Employee> list = Arrays.asList(new Employee("张三", 12),
				new Employee("李四", 15),
				new Employee("王五", 12),
				new Employee("赵六", 33),
				new Employee("赵六", 33),
				new Employee("张琪", 33),
				new Employee("李白", 25)
				);
		/**
		 * 规约 -reduce 合并求值
		 * 累计计算当前公司年龄总和 map reduce联合使用
		 */
		List<Integer> list2 = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
		Integer reduce = list2.stream().reduce(0,(x,y)->x+y);
		System.out.println(reduce);
		Optional<Integer> total = list.stream().map(Employee::getAge).reduce((x,y)->x+y);
		System.out.println("======reduce====="+total.get());
		
		/**
		 * 收集-collect
		 * 将流中的元素转换为其他形式，接收一个Collector接口的实现，用于Stream中元素的汇总方法
		 */
		//将list转换成一个name做键，Employee做值的HashMap
		Map<String, Employee> collect = list.stream().collect(Collectors.toMap(Employee::getName, Function.identity(),(k1,k2)->k2));
		System.out.println(collect.keySet());
		//求总数
		Long count = list.stream().collect(Collectors.counting());
		System.out.println("=====count====="+count);
		//年龄的平均值
		Double averagingInt = list.stream().collect(Collectors.averagingInt(Employee::getAge));
		System.out.println("=====averagingInt====="+averagingInt);
		//年龄总和
		Double sum = list.stream().collect(Collectors.summingDouble(Employee::getAge));
		System.out.println("sum=========="+sum);
		//年龄最小值
		Optional<Employee> minAge = list.stream().collect(Collectors.minBy((x1,x2)->Integer.compare(x1.getAge(), x2.getAge())));
		//按照年龄分组
		Map<Integer, List<Employee>> collect2 = list.stream().collect(Collectors.groupingBy(Employee::getAge));
		System.out.println(collect2.get(33));
		//多级分组
		Map<Integer, Map<String, List<Employee>>> collect3 = list.stream().collect(Collectors.groupingBy(Employee::getAge,Collectors.groupingBy(Employee::getName)));
		System.out.println(collect3.get(33));
	}
}
